﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SmartMathLibrary.LinearEquationSolvers;

namespace SmartMathLibrary.LinearEquationSolvers
{
    /// <summary>
    /// This class groups all available splitting methods to solve complex linear systems in an 
    /// iterative way.
    /// </summary>
    [Serializable]
    public abstract class AbstractComplexSplittingMethod
    {
        /// <summary>
        /// A field, which describes a possible precision error during the calculation.
        /// </summary>
        private bool precisionError;

        /// <summary>
        /// The coefficient matrix to solve.
        /// </summary>
        private readonly ComplexLinearEquationSystem equationSystem;

        /// <summary>
        /// Initializes a new instance of the <see cref="AbstractRealSplittingMethod"/> class.
        /// </summary>
        /// <param name="coefficientMatrix">The coefficient matrix of the equation system to solve.</param>
        /// <param name="resultVector">The result vector of the equation system to solve.</param>
        protected AbstractComplexSplittingMethod(ComplexMatrix coefficientMatrix, GeneralComplexVector resultVector)
        {
            this.precisionError = false;
            this.equationSystem = new ComplexLinearEquationSystem(coefficientMatrix, resultVector);
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="AbstractRealSplittingMethod"/> class.
        /// </summary>
        /// <param name="equationSystem">The equation system to solve.</param>
        protected AbstractComplexSplittingMethod(ComplexLinearEquationSystem equationSystem)
        {
            this.precisionError = false;
            this.equationSystem = new ComplexLinearEquationSystem(equationSystem);
        }

        /// <summary>
        /// Gets a value indicating whether a precision error occurs during the calculation.
        /// </summary>
        /// <value>True if a precision error occurs during the calculation otherwise, false.</value>
        public bool PrecisionError
        {
            get { return this.precisionError; }
        }

        /// <summary>
        /// Gets or sets the coefficient matrix of the equation system to solve.
        /// </summary>
        /// <value>The coefficient matrix of the equation system to solve.</value>
        public ComplexMatrix CoefficientMatrix
        {
            get { return this.equationSystem.CoefficientMatrix; }
            set { this.equationSystem.CoefficientMatrix = value; }
        }

        /// <summary>
        /// Gets or sets the result vector of the equation system to solve.
        /// </summary>
        /// <value>The result vector of the equation system to solve.</value>
        public GeneralComplexVector ResultVector
        {
            get { return this.equationSystem.ResultVector; }
            set { this.equationSystem.ResultVector = value; }
        }

        /// <summary>
        /// Solves the linear system, which is defined by the coefficient matrix.
        /// </summary>
        /// <returns>The solved linear system.</returns>
        public abstract GeneralComplexVector SolveLinearSystem();

        /// <summary>
        /// Solves the linear system, which is defined by the coefficient matrix.
        /// </summary>
        /// <param name="startValues">The start values of the calculation in form of a vector.</param>
        /// <returns>The solved linear system.</returns>
        public abstract GeneralComplexVector SolveLinearSystem(GeneralComplexVector startValues);

        /// <summary>
        /// Solves the linear system, which is defined by the coefficient matrix.
        /// </summary>
        /// <param name="startValues">The start values of the calculation in form of a vector.</param>
        /// <param name="maxIterations">The maximum of iterations for the calculation.</param>
        /// <returns>The solved linear system.</returns>
        public abstract GeneralComplexVector SolveLinearSystem(GeneralComplexVector startValues, int maxIterations);

        /// <summary>
        /// Solves the linear system, which is defined by the coefficient matrix.
        /// </summary>
        /// <param name="startValues">The start values of the calculation in form of a vector.</param>
        /// <param name="maxIterations">The maximum of iterations for the calculation.</param>
        /// <param name="precision">The precision of the results to calculate.</param>
        /// <returns>The solved linear system.</returns>
        public abstract GeneralComplexVector SolveLinearSystem(GeneralComplexVector startValues, int maxIterations,
                                                               double precision);

        /// <summary>
        /// Checks if the splitting method surely converge.
        /// </summary>
        /// <returns>True if the splitting method surely converge otherwise, false.</returns>
        public bool CheckConverge()
        {
            bool strictlyGreater = false;
            ComplexToRealLinearSystemTransform complexRealTransform =
                new ComplexToRealLinearSystemTransform(this.equationSystem);
            LinearEquationSystem equationSystem = complexRealTransform.TransformToRealEquationSystem();

            for (int i = 0; i < this.equationSystem.CoefficientMatrix.Rows; i++)
            {
                double tempuri = 0;
                double compareValue =
                    Math.Abs(equationSystem.CoefficientMatrix.GetValueAtPosition(new MatrixPosition(i, i)));

                for (int j = 0; j < this.equationSystem.CoefficientMatrix.Columns; j++)
                {
                    tempuri +=
                        Math.Abs(equationSystem.CoefficientMatrix.GetValueAtPosition(new MatrixPosition(i, j)));
                }

                if (compareValue > (tempuri - compareValue))
                {
                    strictlyGreater = true;
                }
                else if (compareValue < (tempuri - compareValue))
                {
                    return false;
                }
            }

            return strictlyGreater;
        }

        /// <summary>
        /// Amalgamates the real number vector into a complex vector.
        /// </summary>
        /// <param name="vector">The vector to amalgamate.</param>
        /// <returns>The amalgamated complex vector.</returns>
        protected internal static GeneralComplexVector AmalgamateComplexVector(GeneralVector vector)
        {
            int offset = (vector.Count / 2);
            GeneralComplexVector result = new GeneralComplexVector(vector.Count / 2);

            for (int i = 0; i < result.Count; i++)
            {
                result[i] = new ComplexNumber(vector[i], vector[offset + i]);
            }

            return result;
        }

        /// <summary>
        /// Sets the precision error property.
        /// </summary>
        /// <param name="value">The precision error property value.</param>
        protected void SetPrecisionError(bool value)
        {
            this.precisionError = value;
        }
    }
}